package com.bango.flex{
	
	import flash.errors.IllegalOperationError;
	import flash.system.Capabilities;
	import flash.utils.Dictionary;
	
	import spark.components.TextArea;

	public class BangoAgentImpl {
		
		private var _postOnEndSession:Boolean = true;
		private var _isSessionEnded:Boolean = false;
		private var _logger:BangoLogger = new BangoLogger();
		private var _debugArea:TextArea;
		
		private var _loggingLevels:Array= new Array();
		private var _loggingLevel:Number= BangoLoggingLevel.ALL;
		private var _activeLoggingLevels:Array = [0];
		
		public function set loggingLevel(level:Number):void{
			_loggingLevel=level;
			_activeLoggingLevels = getOrdinalIndices(level);
		}
		
		public function get loggingLevel():Number{
			return _loggingLevel;
		}
		
		public function setLoggingLevelEvents(level:Number,events:Array):void{
			var levelIndex:Number = getOrdinalIndex(level);
			if(levelIndex >= 0 && levelIndex < BangoLoggingLevel.LEVELS.length)
				_loggingLevels[levelIndex] = events;
		}
		
		private function getOrdinalIndices(level:Number):Array{
			var indices:Array = new Array();
			var index:Number = 1;
			for each(var lvl:Number in BangoLoggingLevel.LEVELS){
				if((lvl & level) == lvl)
					indices.push(index);
				index++;
			}
			return indices;	
		}
		
		private function getOrdinalIndex(level:Number):Number{
			var indices:Array = getOrdinalIndices(level);
			if(indices.length > 0)
				return indices[0];
			return -1;
		}
		
		private function isEventAllowed(eventName:String):Boolean{
			if(_loggingLevel == BangoLoggingLevel.ALL)
				return true;
			
			if(eventName == BangoSessionEvents.SESSION_START || eventName == BangoSessionEvents.SESSION_END || eventName == BangoSessionEvents.SESSION_IDLE_START  || eventName == BangoSessionEvents.SESSION_IDLE_END)
				return true;
			
			for each(var lvl:Number in _activeLoggingLevels){
				
				if(lvl ==  BangoLoggingLevel.ALL)
					return true;
				
				if(!_loggingLevels[lvl])
					break;
				
				for each(var event:String in _loggingLevels[lvl]){
					if(event == eventName)
						return true;
				}
			}
			return false;
		}
		
		/**
		 * Gets whether not events are being logged.
		 * 
		 * @return Whether not events are being logged.
		 */
		public function get logEnabled():Boolean{
			return _logger.logEnabled;
		}
		
		/**
		 * Enables or disables the logging of events.
		 * 
		 * @param logEvents
		 *            Should events be logged.
		 */
		public function set logEnabled(logEvents:Boolean):void{
			_logger.logEnabled = logEvents ;
		}
		
		/**
		 * Gets whether or not database connections are persistent.
		 * 
		 * @return Whether or not database connections are persistent.
		 */
		public function get persistDatabaseConnection():Boolean{
			return _logger.persistDatabaseConnection;
		}
		
		/**
		 * Enables or disables the persistence of database connections.
		 * 
		 * @param persistConn
		 *            Should database connection remain open.
		 */
		public function set persistDatabaseConnection(persistConn:Boolean):void{
			_logger.persistDatabaseConnection = persistConn;
		}
		
		/**
		 * Gets Whether or not should be used the offline database.
		 * 
		 * @return Whether or not should be used the offline database.
		 */
		public function get useOfflineStorage():Boolean{
			return _logger.useOfflineStorage;
		}
		
		/**
		 * Enables or disables the use of the offline database.
		 * 
		 * @param useOff
		 *            Should database connection remain open.
		 */
		public function set useOfflineStorage(useOff:Boolean):void{
			_logger.useOfflineStorage = useOff;
		}
		
		/**
		 * Gets the application version to be used.
		 * 
		 * @return The application version to be used.
		 */
		public function get applicationVersion():String{
			return _logger.applicationVersion;
		}
		
		/**
		 * The application version to be used, if this is not supplied then the
		 * application version number of the application is used.
		 * 
		 * @param applicationVersion
		 *            The application version to be used.
		 */
		public function set applicationVersion(applicationVersion:String):void{
			_logger.applicationVersion = applicationVersion;
		}
		
		/**
		 * Gets the unique identifier for the user of the application
		 * 
		 * @return The unique identifier for the user.
		 */
		public function get userId():String{
			return _logger.userId;
		}
		
		/**
		 * The unique identifier that you have for the user of the application. For
		 * example, this could be an email or a GUID or just a numerical value.
		 * 
		 * @param userId
		 */
		public function set userId(userId:String):void{
			_logger.userId = userId;
		}
		
		/**
		 * Gets the URL to post the data to.
		 * 
		 * @return The URL to post the data to.
		 */
		public function get postURL():String{
			return _logger.postURL;
		}
		
		/**
		 * The URL to post the data to.
		 * 
		 * @param postURL
		 *            The URL to post the data to.
		 */
		public function set postURL(postURL:String):void{
			_logger.postURL = postURL;
		}
		
		/**
		 * Gets the type of application.
		 * 
		 * @return The type of application, for example "Lite"
		 */
		public function get applicationType():String{
			return _logger.applicationType;
		}
		
		/**
		 * The type of application.
		 * 
		 * @param appType
		 *            The type of application, for example "Lite".
		 */
		public function set applicationType(appType:String):void{
			_logger.applicationType = appType;
		}
		
		/**
		 * Gets the method used to distribute the application.
		 * 
		 * @return The method used to distribute the application, for example
		 *         "AppStore".
		 */
		public function get applicationChannel():String{
			return _logger.applicationChannel;
		}
		
		/**
		 * The method used to distribute the application.
		 * 
		 * @param appChannel
		 *            The method used to distribute the application, for example
		 *            "AppStore".
		 */
		public function set applicationChannel(appChannel:String):void{
			_logger.applicationChannel = appChannel;
		}
		
		
		/**
		 * Gets the maximum number of events a profile can hold, default is 50. 
		 * Breaking a profile is important to avoid big requests to the server
		 * 
		 * @return the number of maximum events for each profile.
		 * 
		 */
		
		public function get maximumEventsPerProfile():Number
		{
			return _logger.maximumEventsPerProfile;
		}
		
		/**
		 * Sets the maximum number of events a profile can hold, default is 50. 
		 * Breaking a profile is important to avoid big requests to the server
		 * 
		 * @param maxEvents
		 *            The number of maximum events
		 * 
		 */
		public function set maximumEventsPerProfile(maxEvents:Number):void
		{
			_logger.maximumEventsPerProfile = maxEvents;
		}
		
		
		/**
		 * Gets whether or not an attempt should be made to discover the user’s
		 * location.
		 * 
		 * @return Whether or not an attempt should be made to discover the user’s
		 *         location.
		 */
		public function get findLocation():Boolean{
			return _logger.findLocation;
		}
		
		/**
		 * Sets whether or not an attempt should be made to discover the user’s
		 * location.
		 * 
		 * @param findLoc 
		 * 			Whether or not an attempt should be made to discover the user’s
		 *          location.
		 */
		public function set findLocation(findLoc:Boolean):void{
			_logger.findLocation = findLoc;
		}
		
		/**
		 * Whether events will be posted on end session
		 * @return Whether events will be posted on end session
		 */
		public function get postOnEndSession():Boolean{
			return _postOnEndSession;
		}
		
		/**
		 * Whether events will be posted on end session
		 * @param postOnEndSess Set whether events will be posted on end session
		 */
		public function set postOnEndSession(postOnEndSess:Boolean):void{
			_postOnEndSession = postOnEndSess;
		}
		
		/**
		 * Gets the current device version.
		 * 
		 * @return the current device verson.
		 */
		public function get deviceVersion():String
		{
			return _logger.deviceVersion;
		}
		
		/**
		 * Change the device version that will be sent 
		 * @param version Set the device version
		 */
		public function set deviceVersion(version:String):void
		{
			_logger.deviceVersion = version;
		}
		
		/**
		 * Gets the current device version.
		 * 
		 * @return the current device verson.
		 */
		public function get operatingSystemVersion():String
		{
			return _logger.operatingSystemVersion;
		}
		
		/**
		 * Change the device version that will be sent 
		 * @param osVersion Set the device operating system version
		 */
		public function set operatingSystemVersion(osVersion:String):void
		{
			_logger.operatingSystemVersion = osVersion;
		}
		
		/**
		 * Gets the current device operating system.
		 * 
		 * @return the current device operating system.
		 */
		public function get operatingSystem():String
		{
			return _logger.operatingSystem;
		}
		
		/**
		 * Change the device operating system that will be sent 
		 * @param os Set the device operating system
		 */
		public function set operatingSystem(os:String):void
		{
			_logger.operatingSystem = os;
		}
		
		/**
		 * Gets the current product.
		 * 
		 * @return the current product.
		 */
		public function get product():String
		{
			return _logger.product;
		}
		
		/**
		 * Change the product that will be sent 
		 * @param product Set the product
		 */
		public function set product(product:String):void
		{
			_logger.product = product;
		}
		
		/**
		 * Gets the current device model.
		 * 
		 * @return the current device model.
		 */
		public function get deviceModel():String
		{
			return _logger.deviceModel;
		}
		
		/**
		 * Change the device model that will be sent 
		 * @param model Set the device model
		 */
		public function set deviceModel(model:String):void
		{
			_logger.deviceModel = model;
		}
		
		/**
		 * Gets the current device make.
		 * 
		 * @return the current device make.
		 */
		public function get deviceMake():String
		{
			return _logger.deviceMake;
		}
		
		/**
		 * Change the device make that will be sent 
		 * @param make Set the device make
		 */
		public function set deviceMake(make:String):void
		{
			_logger.deviceMake = make;
		}
		/**
		 * Gets the current device brand.
		 * 
		 * @return the current device brand.
		 */
		
		public function get deviceBrand():String
		{
			return _logger.deviceBrand;
		}
		
		/**
		 * Change the device brand that will be sent 
		 * @param brand Set the device brand
		 */
		public function set deviceBrand(brand:String):void
		{
			_logger.deviceBrand = brand;
		}
		
		/**
		 * Send the logged events to Bango
		 */
		public function postEvents():void{
			_logger.postEvents(false,false);
		}
		
		/**
		 * Initiates logging, this must be called after the configuration methods
		 * but before any other logging or any other session methods.
		 * 
		 * @param applicationKey
		 *            The ApplicationId that has been set up for the application
		 *            using the management tools.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 * 
		 */
		public function startSession(applicationKey:String, eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			
			if(applicationKey == null || applicationKey.length < 1){
				throw new ArgumentError("API KEY NOT SPECIFIED");
			}
			
			try{
				_isSessionEnded = false;
				
				if(!_logger.isInitialized){
					_logger.initLogger(applicationKey);
				}
				
				if(!_logger.currentProfileHasEvent(BangoSessionEvents.SESSION_START)){
					_logger.logEventStart(eventValue,eventDetail,eventParameters);
				}
				
				if(_logger.useOfflineStorage){
					_logger.postEvents(true,false);
				}			
				
			}catch(ex:Error){ trace("Init Error" + ex.message + ex.getStackTrace()); }
		}
		
		/**
		 * Indicates that the session has ended after the app has been reduced and sends the events to the server.
		 * 
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function endSession(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			commitEndSession(eventValue,eventDetail,eventParameters,false);
		}
		
		
		/**
		 * Indicates that the session has ended and the app has been closed. If on android sends the data to the server.
		 * 
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function closeApplication(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			commitEndSession(eventValue,eventDetail,eventParameters,true);
		}
		
		
		private function commitEndSession(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null,closing:Boolean = false):void{
			try{
				_logger.isInitialized = false;
				_isSessionEnded = true;
				_logger.logEventEnd(eventValue,eventDetail,eventParameters);
				_logger.EndLocationUpdates();
				if((_postOnEndSession && closing == false) ||
					(_postOnEndSession && closing == true && Capabilities.os.toLowerCase().indexOf("android") >= 0)){
					_logger.postEvents(false,true);
				}
			}catch(ex:Error){}
		}
		
		/**
		 * Logs a generic event.
		 * 
		 * @param eventName
		 *            The event name is identifier for the type of event that you
		 *            would like to record. You can create your own event names
		 *            simply sending it in this field.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onEvent(eventName:String,eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			try{
				if(!isEventAllowed(eventName))
					return;
				_logger.logEvent(eventName,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Logs an onDemandVideoStart event.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onDemandVideoStart(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			if(eventParameters == null){
				eventParameters = new Dictionary();
			}
			eventParameters[BangoConstants.VIDEO_PARAMETERNAME]= BangoConstants.VIDEO_ONDEMANDPARAMETER;
			try{
				onEvent(BangoSessionEvents.EVENT_VIDEOSTART,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Logs an onDemandVideoStop event.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onDemandVideoStop(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			if(eventParameters == null){
				eventParameters = new Dictionary();
			}
			eventParameters[BangoConstants.VIDEO_PARAMETERNAME]= BangoConstants.VIDEO_ONDEMANDPARAMETER;
			try{
				onEvent(BangoSessionEvents.EVENT_VIDEOSTOP,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Logs an onLiveVideoStart event.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onLiveVideoStart(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null ):void{
			if(eventParameters == null){
				eventParameters = new Dictionary();
			}
			eventParameters[BangoConstants.VIDEO_PARAMETERNAME]= BangoConstants.VIDEO_LIVEPARAMETER;
			try{
				onEvent(BangoSessionEvents.EVENT_VIDEOSTART,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Logs an onLiveVideoStop event.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onLiveVideoStop(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			if(eventParameters == null){
				eventParameters = new Dictionary();
			}
			eventParameters[BangoConstants.VIDEO_PARAMETERNAME]= BangoConstants.VIDEO_LIVEPARAMETER;
			try{
				onEvent(BangoSessionEvents.EVENT_VIDEOSTOP,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Indicates that the session has entered an idle state, for example the
		 * application has lost focus.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onIdle(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			try{
				onEvent(BangoSessionEvents.SESSION_IDLE_START,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Indicates that the session has resumed after an idle state, for example
		 * the application has gained focus after losing focus.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onResume(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			try{
				onEvent(BangoSessionEvents.SESSION_IDLE_END,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Logs a search event.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onSeach(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			try{
				onEvent(BangoSessionEvents.EVENT_SEARCH,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Increments the page view count.
		 * @param eventValue
		 *            Optional. This is a value that you can associate with the event.
		 * @param eventDetail
		 *            Optional. Any extra detail that you would like to attach to the event.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onPageView(eventValue:String = "",eventDetail:String = "",eventParameters:Dictionary = null):void{
			try{
				onEvent(BangoSessionEvents.EVENT_PAGEVIEW,eventValue,eventDetail,eventParameters);
			}catch(ex:Error){}
		}
		
		/**
		 * Logs an error event.
		 * 
		 * @param errorId
		 *            The id you want to assign to the error.
		 * @param errorClass
		 *            The class the error occurred in.
		 * @param message
		 *            Any extra detail that you would like to attach to the error.
		 * @param eventParameters
		 *            Optional. A hashtable collection of event parameters.
		 */
		public function onError(message:String,errorId:String, errorClass:String,eventParameters:Dictionary = null):void{
			try{
				_logger.logEventError(errorId,errorClass,message,eventParameters);
			}catch(er:Error){
				_logger.LogInternalErrorCritical(er);
			}
		}
		
		/**
		 * Gets the JSON Text of the current post profile.
		 * 
		 * @return The JSON Text of the current post profile.
		 */	
		public function viewBatch():String{
			var batch:String = "";
			try{
				batch = _logger.viewBatch();
			}catch(ex:Error){
				trace("Error" + ex.message + ex.getStackTrace());
				batch = "Error" + ex.message + ex.getStackTrace();
			}
			return batch;
		}
		
		
		
		public function debug(str:String):void{
			trace(str);
			if(debugArea != null)
				debugArea.appendText(str+"\n");
		}
		
		public function get debugArea():TextArea{
			return _debugArea;
		}
		
		public function set debugArea(value:TextArea):void{
			_debugArea = value;
		}
		
	}
}